/*  File Name	: MySqlDB.cpp
 *  Author		: Javie Pong
 *	 E-mail		: 823960168@qq.com
 *  Created on	: 2012-7-31
 *  Description:
 */

#include <iostream>
#include <sstream>
#include <stdlib.h>
#include <stdio.h>
#include "MySqlDB.h"
#include "Logger.h"
#include "Cmd.h"
#include "DBRowName.h"
#include "Configurator.h"
#include "ComDefine.h"
#include "EnginePlayer.h"
#include "ChatMonitor.h"

#include "cppconn/prepared_statement.h"
#include "cppconn/metadata.h"
#include "cppconn/resultset_metadata.h"
#include "cppconn/exception.h"
#include "cppconn/warning.h"

//MySqlDB* MySqlDB::instance_ = NULL;

MySqlDB::MySqlDB()
{
	this->driver_ = NULL;
	this->local_conn_pool_.clear();
	::memset(this->url,'\0',sizeof(this->url));
	this->db_info_.reset();
	this->cur_size_ = 0;
}

MySqlDB::~MySqlDB()
{
	this->destory_conn();
}

const DBInfo &MySqlDB::dbinfo(void)
{
	return this->db_info_;
}
//MySqlDB *MySqlDB::instance(void)
//{
//	if(NULL==instance_)
//	{
//		instance_ = new MySqlDB();
//	}
//	return instance_;
//}

void MySqlDB::destory(ResultSet* rs, Statement* prep_stmt)
{
	try
	{
		if (rs != NULL)
		{
			rs->close();
			DELETE(rs);
		}
		if (prep_stmt != NULL)
		{
			prep_stmt->close();
			DELETE(prep_stmt);
		}
	}
	catch (SQLException& e)
	{
		LOG((ERROR_LOG,"MySqlDB::destory-ErrorCode:[%d],SQLState:[%s] description:[%s]",
				e.getErrorCode(),e.getSQLState().c_str(),e.what()));
	}
}

int MySqlDB::init(std::string ip,int port,std::string db,
		std::string user,std::string pwd)
{
	this->db_info_.set(ip,port,db,user,pwd);
	sprintf(this->url, "tcp://%s:%d/%s", ip.c_str(),port,db.c_str());

	try
	{
		this->driver_ = sql::mysql::get_mysql_driver_instance();
		if (NULL == this->driver_)
		{
			LOG((ERROR_LOG,"MySqlDB::init-->[%s:%d] init driver error",ip.c_str(),port));
			return -1;
		}
//		sql::Connection *conn = this->new_conn();
//		if (NULL == conn)
//		{
//			LOG((ERROR_LOG,"MySqlDB::init--> driver_->connect"));
//			return -1;
//		}
		return this->init_conn();
	}
	catch (SQLException& e)
	{
		LOG((ERROR_LOG,"MySqlDB::init-ErrorCode:[%d],SQLState:[%s] description:[%s]",
				e.getErrorCode(),e.getSQLState().c_str(),e.what()));
	}
	return -1;
}

int MySqlDB::load_account_info(EnginePlayer *player)
{
//	try
//	{
//		MyConnection conn = this->get_conn();
//		if (NULL == conn.conn_)
//		{
//			return -1;
//		}
//		sql::Statement *state = conn.conn_->createStatement();
//		if (NULL == state)
//		{
//			return -1;
//		}
//
//		std::stringstream q_stream;
//		q_stream << "select * from users where roleid='" << player->role_id()
//				<< "';";
//		sql::ResultSet* rs = state->executeQuery(q_stream.str());
//		if (NULL == rs)
//		{
//			this->destory(rs, state);
//			return ERROR_SERVER_PROBLEM;
//		}
//		if (rs->rowsCount() <= 0)
//		{
//			this->destory(rs, state);
//			return ERROR_ACCOUNT_NOT_EXIST;
//		}
//
//		if (rs->next())
//		{
//			player->base_info_.account = rs->getString(UsersRow::ACCOUNT);
//			player->base_info_.name = rs->getString(UsersRow::NAME);
//			player->base_info_.sex = rs->getInt(UsersRow::SEX);
//			//player->base_info_.total_chips = rs->getInt(UsersRow::CHIPS);
//		}
//		this->destory(rs, state);
//		return 0;
//	}
//	catch (SQLException& e)
//	{
//		LOG((ERROR_LOG,"MySqlDB::load_account_info-ErrorCode:[%d],SQLState:[%s] description:[%s]",
//				e.getErrorCode(),e.getSQLState().c_str(),e.what()));
//	}
	return -1;
}

/*
 * return
 *  1: success
 *  0: fail
 */
int MySqlDB::save_account_info(EnginePlayer *player)
{
//	try
//	{
//		MyConnection conn = this->get_conn();
//		if (NULL == conn.conn_)
//		{
//			return -1;
//		}
//		sql::Statement *state = conn.conn_->createStatement();
//		if (NULL == state)
//		{
//			return -1;
//		}
//
//		char sql[512] = { 0 };
//		std::string sql_str =CONFIG_INSTANCE->sql_cfg()[SQL::SAVE_ACCOUNT].asString();
//		sprintf(sql, sql_str.c_str(), player->base_info_.total_chips,player->room_id(),
//				player->table_id(),player->role_id());
//
//		int row = state->executeUpdate(sql);
//		this->destory(NULL, state);
//		return row;
//	}
//	catch (SQLException& e)
//	{
//		LOG((ERROR_LOG,"MySqlDB::save_account_info-ErrorCode:[%d],SQLState:[%s] description:[%s]",
//				e.getErrorCode(),e.getSQLState().c_str(),e.what()));
//	}
	return 0;
}

int MySqlDB::save_chips_record(ChipsRecord *record)
{
	try
	{
		if (NULL == record)
		{
			return -1;
		}
		MyConnection conn = this->get_conn();
		if (NULL == conn.conn_)
		{
			return -1;
		}
		sql::Statement *state = conn.conn_->createStatement();
		if (NULL == state)
		{
			return -1;
		}

		char sql[512] = { 0 };
		std::string sql_str =
				CONFIG_INSTANCE->sql_cfg()[SQL::SAVE_CHIPS_RECORD].asString();
		sprintf(sql, sql_str.c_str(), record->role_id, record->account.c_str(),
				record->time.c_str(), record->type, record->amount, record->total_chips,
				record->cur_bet,record->room, record->table, record->table_index);
		std::cout <<sql<<std::endl;
		int result = state->execute(sql);
		this->destory(NULL, state);
		return result;
	}
	catch (SQLException& e)
	{
		LOG((ERROR_LOG,"MySqlDB::save_chisp_record-ErrorCode:[%d],SQLState:[%s] description:[%s]",
				e.getErrorCode(),e.getSQLState().c_str(),e.what()));
	}
	catch(...)
	{
		LOG((ERROR_LOG,"MySqlDB::save_chisp_record-Unknown error..."));
	}
	return -1;
}

int MySqlDB::chater_load_info(ChatPlayer *info)
{
	try
	{
		MyConnection conn = this->get_conn();
		if (NULL == conn.conn_)
		{
			return -1;
		}
		sql::Statement *state = conn.conn_->createStatement();
		if (NULL == state)
		{
			return -1;
		}

		std::stringstream q_stream;
		q_stream << "select * from lAccount where roleid='" << info->roleid
				<< "';";
		sql::ResultSet* rs = state->executeQuery(q_stream.str());
		if (NULL == rs)
		{
			this->destory(rs, state);
			return ERROR_SERVER_PROBLEM;
		}
		if (rs->rowsCount() <= 0)
		{
			this->destory(rs, state);
			return ERROR_ACCOUNT_NOT_EXIST;
		}

		if (rs->next())
		{
			info->table = rs->getInt(UsersRow::TABLE);
			info->room = rs->getInt(UsersRow::ROOM);
			info->name = rs->getString(UsersRow::ACCOUNT);
		}
		this->destory(rs, state);
		return 0;
	}
	catch (SQLException& e)
	{
		LOG((ERROR_LOG,"MySqlDB::load_account_info-ErrorCode:[%d],SQLState:[%s] description:[%s]",
				e.getErrorCode(),e.getSQLState().c_str(),e.what()));
	}
	return -1;
}

int MySqlDB::update_tableid(EnginePlayer *player)
{
	try
	{
		MyConnection conn = this->get_conn();
		if (NULL == conn.conn_)
		{
			return -1;
		}
		sql::Statement *state = conn.conn_->createStatement();
		if (NULL == state)
		{
			return -1;
		}

		char sql[512] = { 0 };
		std::string sql_str =CONFIG_INSTANCE->sql_cfg()[SQL::SAVE_TABLE].asString();
		sprintf(sql, sql_str.c_str(),player->table_id(),player->role_id());

		int row = state->executeUpdate(sql);
		this->destory(NULL, state);
		return row;
	}
	catch (SQLException& e)
	{
		LOG((ERROR_LOG,"MySqlDB::update_tableid-ErrorCode:[role:%d][%d],SQLState:[%s] description:[%s]",
				player->role_id(),e.getErrorCode(),e.getSQLState().c_str(),e.what()));
	}
	return 0;
}

int MySqlDB::update_roomid(EnginePlayer *player)
{
	try
	{
		MyConnection conn = this->get_conn();
		if (NULL == conn.conn_)
		{
			return -1;
		}
		sql::Statement *state = conn.conn_->createStatement();
		if (NULL == state)
		{
			return -1;
		}

		char sql[512] = { 0 };
		std::string sql_str =
				CONFIG_INSTANCE->sql_cfg()[SQL::SAVE_ROOM].asString();
		sprintf(sql, sql_str.c_str(), player->room_id(), player->role_id());

		int row = state->executeUpdate(sql);
		this->destory(NULL, state);
		return row;
	} catch (SQLException& e)
	{
		LOG((ERROR_LOG,"MySqlDB::update_roomid-ErrorCode:[role:%d][%d],SQLState:[%s] description:[%s]",
				player->role_id(),e.getErrorCode(),e.getSQLState().c_str(),e.what()));
	}
	return 0;
}

/////////////////////////connect pool///////////////////////////

int MySqlDB::init_conn(void)
{
	LockGuardReturn(RT_MUTEX,lock,this->rt_mutex_,-1);
	sql::Connection* conn = NULL;
	for(int i=0;i<MAX_CONNECT/2;i++)
	{
		conn = this->new_conn();
		if(NULL!=conn)
		{
			this->local_conn_pool_.push_back(conn);
			this->cur_size_++;
		}
		else
		{
			return -1;
		}
	}
	return 0;
}

void MySqlDB::destory_conn(void)
{
	LockGuardReturn(RT_MUTEX,lock,this->rt_mutex_,;);
	sql::Connection* conn = NULL;
	int size = this->local_conn_pool_.size();
	for(int i=0;i<size;i++)
	{
		conn = this->local_conn_pool_.front();
		this->local_conn_pool_.pop_front();
		if(NULL!=conn)
		{
			conn->close();
			DELETE(conn);
		}
	}
}

MyConnection MySqlDB::get_conn(void)
{
	sql::Connection* conn = NULL;
	LockGuardReturn(RT_MUTEX,lock,this->rt_mutex_,NULL);
	if(this->local_conn_pool_.size()>0)
	{
		conn = this->local_conn_pool_.front();
		this->local_conn_pool_.pop_front();
		if(conn->isClosed())
		{
			DELETE(conn);
			conn = this->new_conn();
		}
		if(NULL==conn)
		{
			this->cur_size_--;
		}
	}
	else
	{
		if(this->cur_size_<MAX_CONNECT)
		{
			conn = this->new_conn();
			if(NULL!=conn)
			{
				this->cur_size_++;
			}
		}
		else
		{
			LOG((ERROR_LOG,"MySqlDB::get_conn-->max connection"))
		}
	}
	return 	MyConnection(this,conn);
}

void MySqlDB::release_conn(sql::Connection* conn)
{
	LockGuardReturn(RT_MUTEX,lock,this->rt_mutex_,;);
	if(NULL!=conn)
	{
		this->local_conn_pool_.push_back(conn);
	}
}

sql::Connection* MySqlDB::new_conn(void)
{
	try
	{
		sql::Connection *conn = NULL;
		conn = this->driver_->connect(this->url, this->db_info_.user, this->db_info_.pwd);
		if (NULL == conn)
		{
			LOG((ERROR_LOG,"MySqlDB::new_conn-->can't connect [%s][%s:%s]",this->url,
					this->db_info_.user.c_str(),this->db_info_.pwd.c_str()));
			return NULL;
		}
		return conn;
	}
	catch (SQLException& e)
	{
		LOG((ERROR_LOG,"MySqlDB::new_conn-ErrorCode:[%d],SQLState:[%s] description:[%s]",
				e.getErrorCode(),e.getSQLState().c_str(),e.what()));
	}
	return NULL;
}

int MySqlDB::load_name_map(AccountMap &map)
{
	try
	{
		MyConnection conn = this->get_conn();
		if (NULL == conn.conn_)
		{
			return -1;
		}
		sql::Statement *state = conn.conn_->createStatement();
		if (NULL == state)
		{
			return -1;
		}

		std::stringstream q_stream;
		q_stream << "select * from name_key ";
		sql::ResultSet* rs = state->executeQuery(q_stream.str());
		if (NULL == rs)
		{
			this->destory(rs, state);
			return ERROR_SERVER_PROBLEM;
		}
		if (rs->rowsCount() <= 0)
		{
			this->destory(rs, state);
			return ERROR_ACCOUNT_NOT_EXIST;
		}

		while (rs->next())
		{
			DBPlayer player;
			player.account(rs->getString(NameKey::ACCOUNT));
			player.dbip = rs->getString(NameKey::IP);
			player.dbport = rs->getInt(NameKey::PORT);
			player.role_id(rs->getInt(NameKey::ROLE_ID));
			map.insert(std::pair<std::string,DBPlayer>(player.account(),player));
		}
		this->destory(rs, state);
		return 0;
	}
	catch (SQLException& e)
	{
		LOG((ERROR_LOG,"MySqlDB::load_account_info-ErrorCode:[%d],SQLState:[%s] description:[%s]",
				e.getErrorCode(),e.getSQLState().c_str(),e.what()));
	}
	return -1;
}

////////////////////////////////////////////////////////////////////

//int MySqlDB::load_account_info()
//{
//	sql::ResultSet* rs=this->query("select * from nas");
//
//	LOG((INFO_LOG,"rows:%d",rs->rowsCount()));
//
//	int row = 0;
//	while (rs->next())
//	{
//		LOG((INFO_LOG,"%d-%d-%s-%s-",row,rs->getInt("id"),
//				rs->getString("nasname").c_str(),rs->getString("description").c_str()));
//		row++;
//	}
//
//	this->destory(rs);
//	return 0;
//}
//
//int MySqlDB::save_account_info()
//{
//	std::string sql = "insert into nas(ID,NASNAME,SHORTNAME,TYPE,PORTS,SECRET,COMMUNITY,DESCRIPTION) values('2','10.1.1.1','','','','111','','');";
//	this->insert(sql);
//
//	this->load_account_info();
//
//
//	sql = "update nas set description='fdfasdfasd' where nasname='10.1.1.1';";
//	this->update(sql);
//	this->load_account_info();
//
//	std::stringstream sql_stream;
//	sql_stream << "update nas set description='" << "dddddd" << "'where nasname='" << "10.1.1.1'";
//	this->update(sql_stream.str());
//	this->load_account_info();
//
//
//	//use PreparedStatement
//	 PreparedStatement* prep_stmt = NULL;
//
//	 //如果是事物处理，先去掉自动提交
//	 //this->conn()->setAutoCommit(0);
//
//	 //然后
//	 //this->conn()->commit();
//
//
//     //调用赋默认策略存储过程
////     std::string procedure = "CALL updateHostPolicyByModHost(?,?, @ret, @msg)";
////     prep_stmt = this->conn()->prepareStatement(procedure.c_str());
////     prep_stmt->setString(1, install_code.c_str());
////     prep_stmt->setInt(2, 0);
////     prep_stmt->execute();
//
//	 try
//	{
//		sql = "update nas set description=? where nasname=?;";
//		prep_stmt = this->conn()->prepareStatement(sql.c_str());
//
//		prep_stmt->setString(1, "fdfasd12121212");
//		prep_stmt->setString(2, "10.1.1.1");
//
//		if (prep_stmt->executeUpdate() == 0)
//		{
//
//		}
//		this->load_account_info();
//	}
//	 catch(SQLException& e)
//	 {
//		try
//		{
//			this->conn()->rollback();
//		}
//		catch (SQLException& e)
//		{
//
//		}
//	 }
//	return 0;
//}
